/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// TestClient.cpp : Defines the entry point for the console application.
//

//#include "ssi_socket_stream.h"
#include "SSI_ScriptClient.h"
#include "SSI_ConsoleClient.h"
#include "SSI_SocketManager.h"
#include <string>

using namespace std;

#define  DEBUG_BIT    1
#define  ARGS_BIT     2


typedef int (*FUNCPROC)(CEngine*); 
int LoadDLL(const char* Name, CEngine* theEngine){

	HMODULE hLib = 0;
	FUNCPROC Func;
	string aStr = Name;
	hLib = LoadLibrary(aStr.c_str());
	if(!hLib){
		printf("Error: Could not load DLL: %s (See Externals.dat)\n", aStr.c_str());
		return 0;
	}
	Func = (FUNCPROC)GetProcAddress(hLib, "RegisterFunctions");
	if(!Func){
		printf("Error: RegisterFunctions not found in %s\n", aStr.c_str());
		FreeLibrary(hLib);
		return 0;
	}
	(Func)(theEngine);
	return 1;
}



int LoadDLLs(CEngine* theEngine, const char* ExternalFile){
	File aFile;
	String aStr;

	if(!aFile.Open(ExternalFile, "r")){
		printf("Warning: %s (external DLL list) not found\n", ExternalFile);
		return 0;
	}

	while(aFile.ReadLine(aStr) >= 0){

		theEngine->SetPackageName(aStr.Str());
		LoadDLL(aStr.Str(), theEngine);
	}

	return 1;

}

int main(int argc, char* argv[])
{
	//const char* pcHost = "localhost";
	int nPort = 4242;
	int nPrompt = 0;
	bool bPrompt = true;

	int request = 0;

	string u_file;
	string check; 

  //if (argc > 1) {
	//	pcHost = argv[1];
  //}

	if(argc > 1){
		sscanf(argv[1], "%d", &nPort);
	}

	if(argc > 2){
		sscanf(argv[2], "%d", &nPrompt);
	}

	CSocketManager aMgr;
	CSockCmd aCmd;
	aMgr.Init(nPort, 4000, 5000);

	CSocketSSI* aSocket;

	if(0){
		while(aSocket = aMgr.GetConnection()){
			// This actually goes into a new thread

			socket_stream astream;
			astream.SetLogFile("testclient.log");
			astream.SetSocket(aSocket);

			//astream.Init(pcHost, nPort);


			while(1){
			aCmd.Set(SSI_LINE_REQUEST, 0, 0);
			if(!aCmd.SendMe(*aSocket)){
				break;
			}

			if(!aCmd.ReadMe(*aSocket)){
				break;
			}

			if((aCmd.ID != SSI_COMMAND)/* && (aCmd.ID != SSI_DEBUG_CMD)*/){
				aCmd.Set(RSP_NACK, 0, 0);
				aCmd.SendMe(*aSocket);
				//aCmd = aSock.SendCommand((SSI_SOCKET)m_Connection, Ret);
				return 0;
			}

			if(!aCmd.IsValidSequence()){
				cout << "Bad sequence" << endl;
				const char* mess = "Bad Sequence; closing connection";
				aCmd.Set(RSP_NACK, (int)strlen(mess), mess);
			}else{
				aCmd.Set(RSP_ACK, 0, 0);
				cout << "Sequence OK" << endl;
			}
			aCmd.SendMe(*aSocket);

			char* Buff = new char[255];

			if(aCmd.Data){
				Buff = strcpy(Buff, aCmd.Data);
			}else{
				sprintf(Buff, " ");
			}
			cout << "Command Received: " << Buff << endl;
			}
		}
	}else{
		while(aSocket = aMgr.GetConnection()){

			socket_stream astream;
			astream.SetLogFile("testclient.log");
			astream.SetSocket(aSocket);

			// Get type of run (script or console)
			CSockCmd aCmd;
			SSI_INT64 RunType = 0;
			String ScriptPath;
			SSI_INT64 Flags = 0;
			SSI_INT64 RunMode = NORMAL_MODE;

			aCmd.Set(SSI_ACTION_REQUEST, 0, 0);
			if(!aCmd.SendMe(*aSocket)){
				printf("Could not send action request.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(!aCmd.ReadMe(*aSocket)){
				printf("Could not read action response.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(aCmd.ID != SSI_ACTION_RESPONSE){
				printf("Invalid action response.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(aCmd.Size == 0){
				RunType = 0;    // Console mode
			}else{
				RunType = 1;    // Script mode
				ScriptPath = (const char*)(aCmd.Data);
				if(ScriptPath.GetLength() != aCmd.Size){
					String sErr = "Invalid file name";
					aCmd.Set(RSP_NACK, sErr.GetLength(), sErr.Str());
					aCmd.SendMe(*aSocket);
					astream.CloseConnection();
					astream.CleanUp();
					break;
				}
			}


			aCmd.Set(RSP_ACK, 0, 0);
			if(!aCmd.SendMe(*aSocket)){
				printf("Could not send action response ACK.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}


			// Read Flags
			aCmd.Set(SSI_FLAG_REQUEST, 0, 0);
			if(!aCmd.SendMe(*aSocket)){
				printf("Could not send flag request.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(!aCmd.ReadMe(*aSocket)){
				printf("Could not read flag response.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(aCmd.ID != SSI_FLAG_RESPONSE){
				printf("Invalid action response.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

			if(aCmd.Size != 4){
				String sErr = "Invalid flag packet";
				aCmd.Set(RSP_NACK, sErr.GetLength(), sErr.Str());
				aCmd.SendMe(*aSocket);
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}
			Flags = *(int*)(aCmd.Data);
			if(Flags & DEBUG_BIT){
				RunMode = DEBUG_MODE;
			}

			aCmd.Set(RSP_ACK, 0, 0);
			if(!aCmd.SendMe(*aSocket)){
				printf("Could not send flag response ACK.\n");
				astream.CloseConnection();
				astream.CleanUp();
				break;
			}

      String sArgs;
      if(Flags & ARGS_BIT){
			  // Read Flags
			  aCmd.Set(SSI_ARGS_REQUEST, 0, 0);
			  if(!aCmd.SendMe(*aSocket)){
				  printf("Could not send arg request.\n");
				  astream.CloseConnection();
				  astream.CleanUp();
				  break;
			  }

			  if(!aCmd.ReadMe(*aSocket)){
				  printf("Could not read flag response.\n");
				  astream.CloseConnection();
				  astream.CleanUp();
				  break;
			  }

			  if(aCmd.ID != SSI_ARGS_RESPONSE){
				  printf("Invalid action response.\n");
				  astream.CloseConnection();
				  astream.CleanUp();
				  break;
			  }

        sArgs = aCmd.Data;

        aCmd.Set(RSP_ACK, 0, 0);
			  if(!aCmd.SendMe(*aSocket)){
				  printf("Could not send arg response ACK.\n");
				  astream.CloseConnection();
				  astream.CleanUp();
				  break;
			  }
      }

			// Console op, so do console
			if(RunType == 0){

				// This goes into a thread
				CConsoleClient aClient(&astream, &astream);
				astream.SetEngine(aClient.Engine);
				LoadDLLs(aClient.Engine, "Externals.dat");
				aClient.Loop();
				astream.CloseConnection();
				astream.CleanUp();

			}else{
				// This goes into a thread
				CScriptClient aClient(&astream, &astream);
				astream.SetEngine(aClient.Engine);
				LoadDLLs(aClient.Engine, "Externals.dat");
				if(nPrompt){              // need to find a way to add this
					aClient.bPrompt = true;
				}else{
					aClient.bPrompt = false;
				}
				if(aClient.Load(ScriptPath)){
				}else{
          astream.Format("[CLIENT] Client file not found: %s", ScriptPath.Str());
          // send an error
				}

        if(sArgs.GetLength() > 0){
          aClient.EvaluateArguments(sArgs);
        }

				aClient.Run(RunMode);
				astream.CloseConnection();
				astream.CleanUp();

			}
		}

		// We do have to do this... here.
		Factory_ClearData();
	}

  return 0;
}

